/**
 * This class implements the logic behind the BDD for the n-queens problem
 */
import java.util.*;

import net.sf.javabdd.*;

public class QueensLogic {
	
	// Queens BDD Logic
	private QueensBDDLogic queensBDDLogic;
	
	// A BDD factory is used to gain access to the logical operators and make it possible to restrict the BDD
	private BDDFactory factoryBDD;
	
	private int numberOfQueens;
	private int[][] queensboard;

    public void initializeGame(int size) {
    	numberOfQueens = size;
    	
		// Set the dimensions of the board
		this.queensboard = new int[numberOfQueens][numberOfQueens];
		
		// Initializing the BDD factory with advised amount of nodes and cache
		factoryBDD = JFactory.init(2000000, 200000);
		
		// Set the number of variables
		factoryBDD.setVarNum((int) numberOfQueens * numberOfQueens );
		
		// Building the BDD representation of the board. This represent the rules of the game.
		queensBDDLogic = new QueensBDDLogic(factoryBDD, numberOfQueens);
		queensBDDLogic.generateQueenBDDRules();
    }

    // The "physical" representation of the board.
	// -1 represents illegal spot
	// 1 represents a queen
	// 0 represents not restricted spot
    public int[][] getGameBoard() {
        return queensboard;
    }

    public boolean insertQueen(int column, int row) {
    	printConsole("Insert a queen in column: " + column + ", row: " + row);
    	
    	// Registers if the player tries to insert a queen in an illegal field, meaning that a field is either invalid (-1) or already occupied by a queen (1).
    	// The method insertQueen should then do nothing. 
    	// The trace method shows the method insertQueen has been called even though nothing has been changed in the game.
    	if (queensboard[column][row] == -1 || queensboard[column][row] == 1) {
            return true;
        }
        
        // If a legal spot have been selected then a queen is placed on the board
        queensboard[column][row] = 1;
        
        printConsole("The board after we have inserted a queen:");
        printQueensBoard();
        
        // We restrict our current BDD with the newly inserted queen
        queensBDDLogic.restrict(column, row);
		
        // Find valid domains after restricting our BDD
        boolean validDomains[] = queensBDDLogic.getTheValidDomains();
        // Displays the array containing valid (true) and invalid (false) values of the variables in the board
		printConsole("Valid Domains" + Arrays.toString(validDomains));
		
		// Update the board representation with valid domains
		updateTheBoard(validDomains);
		printConsole("Efter updating the board with valid domains:");
		printQueensBoard();
		
		return true;
    }

	private void printConsole(String string) {
		System.out.println(string);
	}

	private void printQueensBoard() {
		
		String boardCell = "";
		for (int row = 0; row < numberOfQueens; row++) {
			for (int column = 0; column < numberOfQueens; column++  ) {
				boardCell += queensboard[column][row] + "\t";
			}
			
			printConsole(boardCell);
			boardCell = "";
		}
	}

	private void updateTheBoard(boolean[] validDomains) {
		int countValidSpots = 0;

		// Update the board to -1 where it is not possible to insert a queen
		for (int column = 0; column < numberOfQueens; column++) {
			for (int row = 0; row < numberOfQueens; row++) {
				
				int variableNumber = queensBDDLogic.getVariableNumber(column, row);
				if (validDomains[variableNumber])
				{
					countValidSpots++;
					continue;
				}
					
				queensboard[column][row] = -1;
			}
		}
		
	
		// When the number of valid spots is the same as the length of the board, there is 
		// only one single solution and a queen is placed in all the valid spots, and the game is finished.
		if (countValidSpots == numberOfQueens)
			updateBoardWithQueens();
	}

	private void updateBoardWithQueens() {
		for (int column = 0; column < numberOfQueens; column++) {
			for (int row = 0; row < numberOfQueens; row++) {
				int value = queensboard[column][row];
				
				if (value == 0 )
					queensboard[column][row] = 1;
			}
		}
	}
}
